#include <cppunit/TestAssert.h>
#include <cppunit/TestFixture.h>
#include <cppunit/TestResult.h>
#include <cppunit/TestSuite.h>
#include <cppunit/TestCaller.h>

#include <cppunit/extensions/AutoRegisterSuite.h>
#include <cppunit/extensions/HelperMacros.h>

#include <terms/util/to_string.hpp>
#include <terms/dl/dlterms.hpp>

class DLClassTest
	: public CppUnit::TestFixture
{
private:
	terms::dl::DLTermFactory<> *factory;

public:
	void setUp()
	{
		factory = new terms::dl::DLTermFactory<>();
	}

public:
	void tearDown()
	{
		delete factory;
	}

public:
	void testAllocate()
	{
		using namespace terms;
		using namespace terms::dl;

		const DLClass<> *A = factory->create<DLClass<>>("A");
		CPPUNIT_ASSERT(A != nullptr);
	}

	void testSingleton()
	{
		using namespace terms;
		using namespace terms::dl;

		const DLClass<> *A0 = factory->create<DLClass<>>("A");
		const DLClass<> *A1 = factory->create<DLClass<>>("A");
		CPPUNIT_ASSERT(A0 == A1);
		CPPUNIT_ASSERT(*A0 == *A1);
	}

	void testTermHierarchy()
	{
		using namespace terms;
		using namespace terms::dl;

		const DLClass<> *A = factory->create<DLClass<>>("A");
		CPPUNIT_ASSERT(dynamic_cast<const DLClassExpression<> *>(A) != nullptr);
		CPPUNIT_ASSERT(dynamic_cast<const DLTerm<> *>(A) != nullptr);
		CPPUNIT_ASSERT(dynamic_cast<const Term *>(A) != nullptr);
	}

	void testOrder()
	{
		using namespace terms;
		using namespace terms::dl;

		const DLClass<> *A = factory->create<DLClass<>>("A");
		const DLClass<> *B = factory->create<DLClass<>>("B");

		CPPUNIT_ASSERT(*A < *B);
		CPPUNIT_ASSERT(!(*A > *B));
		CPPUNIT_ASSERT(*B > *A);
		CPPUNIT_ASSERT(!(*B < *A));

		CPPUNIT_ASSERT(*A <= *B);
		CPPUNIT_ASSERT(!(*A >= *B));
		CPPUNIT_ASSERT(*B >= *A);
		CPPUNIT_ASSERT(!(*B <= *A));
	}

	void testEquality()
	{
		using namespace terms;
		using namespace terms::dl;

		const DLClass<> *A = factory->create<DLClass<>>("A");
		const DLClass<> *A2 = factory->create<DLClass<>>("A");
		const DLClass<> *B = factory->create<DLClass<>>("B");

		CPPUNIT_ASSERT(A == A2);
		CPPUNIT_ASSERT(*A == *A2);
		CPPUNIT_ASSERT(*A2 == *A);

		CPPUNIT_ASSERT(!(*A != *A2));
		CPPUNIT_ASSERT(!(*A2 != *A));

		CPPUNIT_ASSERT(B != A);
		CPPUNIT_ASSERT(*A != *B);
		CPPUNIT_ASSERT(*B != *A);
		CPPUNIT_ASSERT(!(*A == *B));
		CPPUNIT_ASSERT(!(*B == *A));
	}


	static CppUnit::Test *
	suite()
	{
		std::unique_ptr<CppUnit::TestSuite> suite_ptr(new CppUnit::TestSuite("DLClassTest"));

		suite_ptr->addTest(new CppUnit::TestCaller<DLClassTest>("testAllocate", &DLClassTest::testAllocate));
		suite_ptr->addTest(new CppUnit::TestCaller<DLClassTest>("testSingleton", &DLClassTest::testSingleton));
		suite_ptr->addTest(new CppUnit::TestCaller<DLClassTest>("testOrder", &DLClassTest::testOrder));
		suite_ptr->addTest(new CppUnit::TestCaller<DLClassTest>("testEquality", &DLClassTest::testEquality));
		suite_ptr->addTest(new CppUnit::TestCaller<DLClassTest>("testTermHierarchy", &DLClassTest::testTermHierarchy));

		return suite_ptr.release();
	}
};

CPPUNIT_TEST_SUITE_REGISTRATION(DLClassTest);

#include "test_main.cc"
